package de.tum.in.eist.im.client;

import java.util.Date;
import java.util.Iterator;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;

import de.tum.in.eist.im.client.domain.Message;
import de.tum.in.eist.im.client.domain.NewMessagesChunk;
import de.tum.in.eist.im.client.domain.User;
/**
 *  Handles main functionality of the chat client.
 *  Provides chat interface and handles user-server communication.
 *  Create Object of this class for new chatroom. 
 * @author Dennis
 *
 */
public class Chat {
		// initialize multi-method used variables
		private User me;
		private TextBox tb;
		private ListBox userList;
		private ListBox chatList;
		private long timestamp;
		private Timer refreshTimer;
		private Date date;
		private DateTimeFormat df = DateTimeFormat.getFormat("HH:mm:ss");

		
		//server rpc
		private ChatServiceAsync chatSvc = GWT.create(ChatService.class);
		
		/**
		 * 
		 * calls setContent() method and defines the user of this chatroom.
		 * 
		 * @param me  the user of this chatroom
		 */
		public Chat(User me) {
			setContent();
			this.me = me;
		}

		/**
		 * 
		 * Provides chat content.
		 * Manages chatscreen layout and initializes content widgets.
		 * 
		 * Reacts send and logout button clicks and keyboard enter-key events in textbox.
		 * 
		 */
		public void setContent(){
			
			//setup login screen
			RootPanel.get("header").clear();
			RootPanel.get("content").clear();
			RootPanel.get("footer").clear();		
			// initialising chat widgets
			Label title = new Label();
			Label userListTitle = new Label();
			userList = new ListBox();
			chatList = new ListBox();
			tb = new TextBox();
			Button sendB = new Button();
			Button logoutB = new Button();
			// managing widget stylenames
			title.addStyleName("chatTitle");
			userListTitle.addStyleName("userListTitle");
			userList.addStyleName("chatUserList");
			chatList.addStyleName("chatList");
			tb.addStyleName("chatTextBox");
			sendB.addStyleName("chatSendButton");
			logoutB.addStyleName("chatLogoutButton");
			

			// managing panels
			HorizontalPanel headerPanel = new HorizontalPanel();
			headerPanel.add(userListTitle);
			headerPanel.add(title);
			RootPanel.get("header").add(headerPanel);
			HorizontalPanel hp1 = new HorizontalPanel();
			hp1.addStyleName("chatContentPanel");
			hp1.add(userList);
			hp1.add(chatList);
			RootPanel.get("content").add(hp1);
			HorizontalPanel hp2 = new HorizontalPanel();
			hp2.addStyleName("chatFooterPanel");
			hp2.add(logoutB);
			hp2.add(tb);
			hp2.add(sendB);
			RootPanel.get("footer").add(hp2);
			// providing widget content
			title.setText("EIST-IM Chat");
			userListTitle.setText("Attendants");
			sendB.setText("Send");
			logoutB.setText("Logout");
			userList.setVisibleItemCount(45);
			chatList.setVisibleItemCount(45);
			getUserList();		
			
/*			not implemented on server
			//querry history
			Date from = new Date();
			getMessageHistory(from);
*/			
			// handle events
		    //keypress enter calls newMessage()
		    tb.addKeyPressHandler(new KeyPressHandler() {
		    	public void onKeyPress(KeyPressEvent event) {
		    		if(event.getCharCode() == KeyCodes.KEY_ENTER)
		    			if(!tb.getText().isEmpty())
		    		newMessage();
		    	}
		    });
		    // Listen for send button clicks
		    sendB.addClickHandler(new ClickHandler() {
		    	public void onClick(ClickEvent event) {
		    		if(!tb.getText().isEmpty())
		    		newMessage();
		    	}
		    });
		    
		    // Listen for logout button clicks
		    logoutB.addClickHandler(new ClickHandler() {
		    	public void onClick(ClickEvent event) {
		    		userLogout();
		    	}
		    });
		    
		    // start newMsg thread
		    newMessageService();

		}
		
		/**
		 * 
		 * Queries userList from the server and calls fillUserList on success.
		 * 
		 * 
		 */
		
		private void getUserList(){
			// initial userList call
			
			// Initialize the service proxy.
		    if (chatSvc == null) {
		      chatSvc = GWT.create(ChatService.class);
		    }
		    // Set up the callback object.
		    AsyncCallback<List<User>> callback = new AsyncCallback<List<User>>() {
		      public void onFailure(Throwable caught) {
		         Window.alert("Retrieving userlist failed.");
		      }

			public void onSuccess(List<User> result) {
			//fill in userListBox
				fillUserList(result);
				
				}
			};
			 // Make the call to the service.
		    chatSvc.getLoggedInUsers(callback);
			
			
			// Setup timer to refresh list automatically.
			refreshTimer = new Timer() {
		      @Override
		      public void run() {
			// Initialize the service proxy.
		    if (chatSvc == null) {
		      chatSvc = GWT.create(ChatService.class);
		    }
		    // Set up the callback object.
		    AsyncCallback<List<User>> callback = new AsyncCallback<List<User>>() {
		      public void onFailure(Throwable caught) {
		         Window.alert("Retrieving userlist failed.");
		      }

			public void onSuccess(List<User> result) {
			//fill in userListBox
				fillUserList(result);
				
				}
			};
		    // Make the call to the service.
		    chatSvc.getLoggedInUsers(callback);
		      }
		    };
		    refreshTimer.scheduleRepeating(10000);
			
			
		}
		/**
		 * 
		 * Fills the userList-widget
		 * 
		 * @param list of logged in users
		 */

		private void fillUserList(List<User> list) {
			Iterator<User> it = list.iterator();
			userList.clear();
			while(it.hasNext()){
				userList.addItem(it.next().getUsername());
			}
		}
		
		/**
		 *  Successive polling message retrieving method. Queries server every 3 seconds for new Messages.
		 *  Calls addMessages on success.
		 * 
		 */
		private void newMessageService() {
			//sucessive polling variant
	      	  date = new Date();
	    	  timestamp = date.getTime();
			// Setup timer to refresh list automatically.
			refreshTimer = new Timer() {
		      @Override
		      public void run() {

		    	  // Initialize the service proxy.
				    if (chatSvc == null) {
				      chatSvc = GWT.create(ChatService.class);
				    }
				    // Set up the callback object.
				    AsyncCallback<NewMessagesChunk> callback = new AsyncCallback<NewMessagesChunk>() {
				      public void onFailure(Throwable caught) {
				    	  Window.alert("Retrieving new messages failed.");
				        
				      }
					public void onSuccess(NewMessagesChunk result) {
							timestamp = result.getTimestamp();
							List<Message> msgList = result.getMessages();
							addMessages(msgList);
						}
					};
				    // Make the call to the service.
				    chatSvc.getNewMessages(timestamp, callback);
		      }
		    };
		    refreshTimer.scheduleRepeating(3000);
			
		}
		/**
		 * 
		 *  Adds given messages to the chatlist
		 * 
		 * @param msgList  List of messages which will be added to the chatlist
		 */
		
		private void addMessages(List<Message> msgList){
			Iterator<Message> it = msgList.iterator();
			while(it.hasNext()){
				Message msg = it.next();
				if(!msg.getSender().equals(me))
				chatList.addItem(msg.getSender().getUsername() + " " + df.format(new Date(msg.getTimestamp())) + ": " + msg.getText());
			}
			
		}
/* not implemented in server		
		private void getMessageHistory(Date from){
			
			 // Initialize the service proxy.
		    if (chatSvc == null) {
		      chatSvc = GWT.create(ChatService.class);
		    }
		    // Set up the callback object.
		    AsyncCallback<List<Message>> callback = new AsyncCallback<List<Message>>() {
		      public void onFailure(Throwable caught) {
		    	 caught.printStackTrace();
		         Window.alert("Retrieving chat history failed. \n" + caught.getMessage());
		      }

			public void onSuccess(List<Message> result) {
			//do something after sending message
				addMessages(result);
				}
			};
		    // Make the call to the service.
			from = new Date();
		    chatSvc.getHistory(from, callback);
			
			
		}
*/		
		/**
		 * 
		 *  Called after send button click or enter-key event in textbox.
		 *  
		 *  Creates new Message and sends it to the server.
		 * 
		 * 
		 */
		private void newMessage(){
			date = new Date();
			
			Message newMsg = new Message(date.getTime(), tb.getText(), me);
			
			  // Initialize the service proxy.
		    if (chatSvc == null) {
		      chatSvc = GWT.create(ChatService.class);
		    }
		    // Set up the callback object.
		    AsyncCallback<Void> callback = new AsyncCallback<Void>() {
		      public void onFailure(Throwable caught) {
		         Window.alert("Sending message failed.");
		      }

			public void onSuccess(Void result) {
			//do something after sending message
				chatList.addItem(me.getUsername() + " " + df.format(date) + ": " + tb.getText());
				tb.setText("");
				}
			};
		    // Make the call to the service.
		    chatSvc.sendMessage(newMsg, callback);
		}
		
		/**
		 * 
		 * Logout the user. Closes chat and switches to login screen.
		 * 
		 */
		private void userLogout(){
			  // Initialize the service proxy.
		    if (chatSvc == null) {
		      chatSvc = GWT.create(ChatService.class);
		    }
		    // Set up the callback object.
		    AsyncCallback<Boolean> callback = new AsyncCallback<Boolean>() {
		      public void onFailure(Throwable caught) {
		         Window.alert("Logout failed.");
		      }

			public void onSuccess(Boolean result) {
			//do something after logout
				if(result) {
				Window.confirm("Successfully logged out.");
				refreshTimer.cancel();
				new Login(); 
				} else Window.confirm("Could not log you out.");
				}
			};
		    // Make the call to the service.
		    chatSvc.logout(me, callback);
			
		}
		
}
